package com.passwordmanager.service.impl;

import com.passwordmanager.dto.password.PasswordEntryDto;
import com.passwordmanager.dto.password.PasswordGenerateRequest;
import com.passwordmanager.model.PasswordEntry;
import com.passwordmanager.model.PasswordStrengthCheck;
import com.passwordmanager.model.User;
import com.passwordmanager.repository.PasswordEntryRepository;
import com.passwordmanager.repository.UserRepository;
import com.passwordmanager.service.PasswordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 密码管理服务实现类
 * 提供密码的加密存储、生成、管理和共享功能
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PasswordServiceImpl implements PasswordService {

    private final PasswordEntryRepository passwordEntryRepository;
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    
    // 从配置文件中读取加密密钥
    @Value("${app.password.encryption.key}")
    private String encryptionKey;
    
    // AES加密所需的密钥
    private SecretKeySpec secretKey;
    private static final String ALGORITHM = "AES";
    // 用于生成随机密码的安全随机数生成器
    private static final SecureRandom RANDOM = new SecureRandom();

    /**
     * 初始化加密密钥
     * 在服务启动时自动执行
     */
    @PostConstruct
    public void init() {
        try {
            // 将配置的密钥转换为字节数组
            byte[] key = encryptionKey.getBytes(StandardCharsets.UTF_8);
            // 使用SHA-1算法处理密钥
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            // 使用128位密钥
            key = Arrays.copyOf(key, 16);
            secretKey = new SecretKeySpec(key, ALGORITHM);
            log.info("加密密钥初始化成功");
        } catch (NoSuchAlgorithmException e) {
            log.error("加密密钥初始化失败", e);
            throw new RuntimeException("加密密钥初始化失败", e);
        }
    }

    /**
     * 生成随机密码
     * @param request 包含密码生成的配置参数
     * @return 生成的随机密码
     */
    @Override
    public String generatePassword(PasswordGenerateRequest request) {
        // 根据用户选择的选项构建字符集
        String chars = "";
        if (request.isIncludeLowerCase()) chars += "abcdefghijklmnopqrstuvwxyz";
        if (request.isIncludeUpperCase()) chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        if (request.isIncludeNumbers()) chars += "0123456789";
        if (request.isIncludeSpecialChars()) chars += "!@#$%^&*()_+-=[]{}|;:,.<>?";

        // 生成指定长度的随机密码
        StringBuilder password = new StringBuilder(request.getLength());
        for (int i = 0; i < request.getLength(); i++) {
            int randomIndex = RANDOM.nextInt(chars.length());
            password.append(chars.charAt(randomIndex));
        }
        return password.toString();
    }

    /**
     * 创建新的密码条目
     * @param passwordDto 密码信息
     * @param username 用户名
     * @return 创建的密码条目
     */
    @Override
    @Transactional
    public PasswordEntryDto createPassword(PasswordEntryDto passwordDto, String username) {
        User user = getUserByUsername(username);
        PasswordEntry passwordEntry = new PasswordEntry();
        updatePasswordEntryFromDto(passwordEntry, passwordDto);
        passwordEntry.setUser(user);
        // 加密密码后存储
        passwordEntry.setEncryptedPassword(encryptPassword(passwordDto.getPassword()));
        return convertToDto(passwordEntryRepository.save(passwordEntry));
    }

    /**
     * 更新密码条目
     * @param id 密码条目ID
     * @param passwordDto 更新的密码信息
     * @param username 用户名
     * @return 更新后的密码条目
     */
    @Override
    @Transactional
    public PasswordEntryDto updatePassword(Long id, PasswordEntryDto passwordDto, String username) {
        PasswordEntry passwordEntry = getPasswordEntryForUser(id, username);
        updatePasswordEntryFromDto(passwordEntry, passwordDto);
        passwordEntry.setEncryptedPassword(encryptPassword(passwordDto.getPassword()));
        return convertToDto(passwordEntryRepository.save(passwordEntry));
    }

    /**
     * 删除密码条目
     * @param id 密码条目ID
     * @param username 用户名
     */
    @Override
    @Transactional
    public void deletePassword(Long id, String username) {
        PasswordEntry passwordEntry = getPasswordEntryForUser(id, username);
        passwordEntryRepository.delete(passwordEntry);
    }

    /**
     * 获取单个密码条目
     * @param id 密码条目ID
     * @param username 用户名
     * @return 密码条目信息
     */
    @Override
    public PasswordEntryDto getPassword(Long id, String username) {
        return convertToDto(getPasswordEntryForUser(id, username));
    }

    /**
     * 获取用户的所有密码条目
     * @param username 用户名
     * @return 密码条目列表
     */
    @Override
    public List<PasswordEntryDto> getAllPasswords(String username) {
        User user = getUserByUsername(username);
        return passwordEntryRepository.findByUser(user)
                .stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    /**
     * 分享密码给其他用户
     * @param passwordId 密码条目ID
     * @param shareWithUsername 被分享的用户名
     * @param ownerUsername 密码所有者用户名
     */
    @Override
    @Transactional
    public void sharePassword(Long passwordId, String shareWithUsername, String ownerUsername) {
        PasswordEntry passwordEntry = getPasswordEntryForUser(passwordId, ownerUsername);
        User shareWithUser = getUserByUsername(shareWithUsername);
        passwordEntry.getSharedWith().add(shareWithUser);
        passwordEntryRepository.save(passwordEntry);
    }

    /**
     * 取消密码分享
     * @param passwordId 密码条目ID
     * @param shareWithUsername 被分享的用户名
     * @param ownerUsername 密码所有者用户名
     */
    @Override
    @Transactional
    public void removePasswordShare(Long passwordId, String shareWithUsername, String ownerUsername) {
        PasswordEntry passwordEntry = getPasswordEntryForUser(passwordId, ownerUsername);
        User shareWithUser = getUserByUsername(shareWithUsername);
        passwordEntry.getSharedWith().remove(shareWithUser);
        passwordEntryRepository.save(passwordEntry);
    }

    /**
     * 获取被分享的密码列表
     * @param username 用户名
     * @return 被分享的密码条目列表
     */
    @Override
    public List<PasswordEntryDto> getSharedPasswords(String username) {
        User user = getUserByUsername(username);
        return passwordEntryRepository.findBySharedWith(user)
                .stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    /**
     * 检查密码强度
     * @param password 待检查的密码
     * @return 密码强度检查结果
     */
    @Override
    public PasswordStrengthCheck checkPasswordStrength(String password) {
        PasswordStrengthCheck strengthCheck = new PasswordStrengthCheck();
        // 检查密码各项特征
        strengthCheck.setLength(password.length());
        strengthCheck.setHasLowerCase(password.matches(".*[a-z].*"));
        strengthCheck.setHasUpperCase(password.matches(".*[A-Z].*"));
        strengthCheck.setHasNumbers(password.matches(".*\\d.*"));
        strengthCheck.setHasSpecialChars(password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{}|;:,.<>?].*"));

        // 计算密码强度得分
        int score = 0;
        if (strengthCheck.getLength() >= 8) score++;
        if (strengthCheck.getLength() >= 12) score++;
        if (strengthCheck.isHasLowerCase()) score++;
        if (strengthCheck.isHasUpperCase()) score++;
        if (strengthCheck.isHasNumbers()) score++;
        if (strengthCheck.isHasSpecialChars()) score++;

        strengthCheck.setScore(Math.min(score, 4));
        
        // 如果密码强度不够，提供改进建议
        if (score < 3) {
            strengthCheck.setSuggestions("建议使用更长的密码，并包含大小写字母、数字和特殊字符");
        }

        return strengthCheck;
    }

    /**
     * 根据用户名获取用户
     */
    private User getUserByUsername(String username) {
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
    }

    /**
     * 获取用户的密码条目
     */
    private PasswordEntry getPasswordEntryForUser(Long id, String username) {
        User user = getUserByUsername(username);
        return passwordEntryRepository.findByIdAndUser(id, user)
                .orElseThrow(() -> new RuntimeException("密码不存在或无权访问"));
    }

    /**
     * 从DTO更新密码条目信息
     */
    private void updatePasswordEntryFromDto(PasswordEntry entry, PasswordEntryDto dto) {
        entry.setTitle(dto.getTitle());
        entry.setUsername(dto.getUsername());
        entry.setWebsite(dto.getWebsite());
        entry.setNotes(dto.getNotes());
    }

    /**
     * 将密码条目转换为DTO
     */
    private PasswordEntryDto convertToDto(PasswordEntry entry) {
        PasswordEntryDto dto = new PasswordEntryDto();
        dto.setId(entry.getId());
        dto.setTitle(entry.getTitle());
        dto.setUsername(entry.getUsername());
        // 解密密码
        dto.setPassword(decryptPassword(entry.getEncryptedPassword()));
        dto.setWebsite(entry.getWebsite());
        dto.setNotes(entry.getNotes());
        dto.setCreatedAt(entry.getCreatedAt());
        dto.setUpdatedAt(entry.getUpdatedAt());
        dto.setLastAccessed(entry.getLastAccessed());
        dto.setShared(!entry.getSharedWith().isEmpty());
        return dto;
    }

    /**
     * 加密密码
     * @param password 原始密码
     * @return 加密后的密码
     */
    private String encryptPassword(String password) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(password.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            log.error("密码加密失败", e);
            throw new RuntimeException("加密失败", e);
        }
    }

    /**
     * 解密密码
     * @param encryptedPassword 加密的密码
     * @return 解密后的原始密码
     */
    private String decryptPassword(String encryptedPassword) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedPassword)), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("密码解密失败", e);
            throw new RuntimeException("解密失败", e);
        }
    }
} 